package com.emirates.spring.banzen2;

import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.opencv.android.Utils;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.utils.Converters;

import com.googlecode.tesseract.android.TessBaseAPI;

import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;
import android.view.ViewDebug.CapturedViewProperty;
import android.widget.Toast;


public class BanzenImageProcessor {
	
	public static Mat processMat(Mat inputMat )
	{
		//Mat intMat = new Mat(inputMat.size(),inputMat.type());
		
	Core.putText(inputMat, "Banzen", new Point(0,10), Core.FONT_HERSHEY_PLAIN ,2, new Scalar(0,0,255));
		
		return getlPreview(inputMat);
	}

	
	public static Mat getlicensePlate(Mat inputMat)
	{
	


		Mat localMat = new Mat(new org.opencv.core.Size(inputMat.cols() / 2,
				inputMat.rows() / 2), inputMat.type());

		Imgproc.resize(inputMat, localMat, localMat.size(), 0, 0,
				Imgproc.INTER_CUBIC);
		Imgproc.resize(localMat, localMat, inputMat.size(), 0, 0,
				Imgproc.INTER_CUBIC);

		Mat gray = new Mat();
		Imgproc.cvtColor(localMat, gray, Imgproc.COLOR_RGB2GRAY);

		Mat blur = new Mat();
		Mat Postblur = new Mat();
		Imgproc.GaussianBlur(gray, blur, new org.opencv.core.Size(5, 5), 0);

		blur.copyTo(Postblur);

		Mat thresh = new Mat();
		Imgproc.threshold(blur, thresh, 128, 255, Imgproc.THRESH_OTSU
				| Imgproc.THRESH_BINARY);

		Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ERODE,
				new Size(3, 3), new Point(-1, -1));
		Imgproc.erode(thresh, thresh, kernel);
		kernel = Imgproc.getStructuringElement(Imgproc.MORPH_DILATE, new Size(
				3, 3), new Point(-1, -1));
		Imgproc.dilate(thresh, thresh, kernel);

		Mat postThresh = new Mat();
		thresh.copyTo(postThresh);

		saveMat(postThresh, "pt");
		
		List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(thresh, contours, new Mat(), Imgproc.RETR_LIST,
				Imgproc.CHAIN_APPROX_SIMPLE);

		List<MatOfPoint> candidates = new ArrayList<MatOfPoint>();

		for (MatOfPoint p : contours) {

			MatOfPoint2f MoP2f = new MatOfPoint2f();
			MatOfPoint2f MoP2f2 = new MatOfPoint2f();

			org.opencv.core.Rect rect = Imgproc.boundingRect(p);
			double aspectRatio = rect.width / rect.height;

			if (Imgproc.contourArea(p) > 5000) {

				p.convertTo(MoP2f, CvType.CV_32FC2);
				Imgproc.approxPolyDP(MoP2f, MoP2f2, 0.02 * Imgproc.arcLength(
						MoP2f, true), true);

				System.out.println(MoP2f2.total());
				if (MoP2f2.total() == 4) {

					MatOfPoint mp = new MatOfPoint();
					mp.fromArray(MoP2f2.toArray());

					if (Imgproc.isContourConvex(mp)) {

						if (aspectRatio >= 1.6 && aspectRatio < 5) {

							double maxCosine = 0;
						
							Point[] points = new Point[4];
							for (int j = 2; j < 5; j++) {

								RotatedRect RotBounds = Imgproc
										.minAreaRect(MoP2f2);
								RotBounds.points(points);

								double cosine = Math.abs((angle(points[j % 4],
										points[j - 2], points[j - 1])));
								maxCosine = (maxCosine > cosine) ? maxCosine
										: cosine;
							}

							if (maxCosine < 0.3) {

								candidates.add(mp);

								Mat subMat = gray.submat(rect);
								Mat mask = Mat.zeros(postThresh.size(), gray
										.type());
								List<MatOfPoint> cntrList = new ArrayList<MatOfPoint>();
								cntrList.add(p);
								Imgproc.drawContours(mask, cntrList, -1,
										new Scalar(255, 255, 255), -1);
								Mat imageROI = new Mat();
								Imgproc.cvtColor(inputMat, inputMat,
										Imgproc.COLOR_RGB2GRAY);
								inputMat.copyTo(imageROI, mask);
							//	saveMat("roi", imageROI);

							
								Mat dskMat = new Mat();
								deskewMat(imageROI, dskMat, MoP2f2);							
								
								ArrayList<ContourData> d=	(ArrayList< ContourData>)extractCandidatesFromPlate(dskMat);
								
								Collections.sort(d,new ContourDataComparator());
								
							BanzenImageProcessor.setMyText(	getText(d));
								
							}
						}
					}
				}

			}

		}

		 Imgproc.drawContours(localMat,candidates , -1, new
		 Scalar(0,255,0),2);
		
	
	return localMat;
	}

	
	private static String myText=""; 
	
	public static String getMyText() {
		return myText;
	}


	public static void setMyText(String myText) {
		BanzenImageProcessor.myText = myText;
	}


	private static String getText(List<ContourData> d)
	{
		   TessBaseAPI baseApi= new TessBaseAPI();
		    Log.i("TESS","PATH IS:"+DATA_PATH);
		    baseApi.init(_path, "eng");
		    
		    String recognizedText="";
		    
		    int i=0;
			for (ContourData di:d)
			{
				Bitmap bmp = Bitmap.createBitmap(di.getMatData().cols(), di.getMatData().rows(), Bitmap.Config.ARGB_8888);
				Utils.matToBitmap(di.getMatData(), bmp,true);
						
				  baseApi.setImage(bmp);
				     recognizedText += baseApi.getUTF8Text();
				saveMat(di.getMatData(), "s");
				
			}
		    
		  
		    baseApi.end();
		    return recognizedText;
	}
	
	public static final String DATA_PATH = Environment
.getExternalStorageDirectory().toString();
	
	private static String _path=DATA_PATH+"/Banzen/";
	
	

	public static Mat getlPreview(Mat inputMat)
	{
	


		Mat localMat = new Mat(new org.opencv.core.Size(inputMat.cols() / 2,
				inputMat.rows() / 2), inputMat.type());

		Imgproc.resize(inputMat, localMat, localMat.size(), 0, 0,
				Imgproc.INTER_CUBIC);
		Imgproc.resize(localMat, localMat, inputMat.size(), 0, 0,
				Imgproc.INTER_CUBIC);

		Mat gray = new Mat();
		Imgproc.cvtColor(localMat, gray, Imgproc.COLOR_RGB2GRAY);

		Mat blur = new Mat();
		Mat Postblur = new Mat();
		Imgproc.GaussianBlur(gray, blur, new org.opencv.core.Size(5, 5), 0);

		blur.copyTo(Postblur);

		Mat thresh = new Mat();
		Imgproc.threshold(blur, thresh, 128, 255, Imgproc.THRESH_OTSU
				| Imgproc.THRESH_BINARY);

		Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ERODE,
				new Size(3, 3), new Point(-1, -1));
		Imgproc.erode(thresh, thresh, kernel);
		kernel = Imgproc.getStructuringElement(Imgproc.MORPH_DILATE, new Size(
				3, 3), new Point(-1, -1));
		Imgproc.dilate(thresh, thresh, kernel);

		Mat postThresh = new Mat();
		thresh.copyTo(postThresh);

		List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(thresh, contours, new Mat(), Imgproc.RETR_LIST,
				Imgproc.CHAIN_APPROX_SIMPLE);

		List<MatOfPoint> candidates = new ArrayList<MatOfPoint>();

		for (MatOfPoint p : contours) {

			MatOfPoint2f MoP2f = new MatOfPoint2f();
			MatOfPoint2f MoP2f2 = new MatOfPoint2f();

			org.opencv.core.Rect rect = Imgproc.boundingRect(p);
			double aspectRatio = rect.width / rect.height;

			if (Imgproc.contourArea(p) > 5000) {

				p.convertTo(MoP2f, CvType.CV_32FC2);
				Imgproc.approxPolyDP(MoP2f, MoP2f2, 0.02 * Imgproc.arcLength(
						MoP2f, true), true);

				System.out.println(MoP2f2.total());
				if (MoP2f2.total() == 4) {

					MatOfPoint mp = new MatOfPoint();
					mp.fromArray(MoP2f2.toArray());

					if (Imgproc.isContourConvex(mp)) {

						if (aspectRatio >= 1.6 && aspectRatio < 5) {

							double maxCosine = 0;
						
							Point[] points = new Point[4];
							for (int j = 2; j < 5; j++) {

								RotatedRect RotBounds = Imgproc
										.minAreaRect(MoP2f2);
								RotBounds.points(points);

								double cosine = Math.abs((angle(points[j % 4],
										points[j - 2], points[j - 1])));
								maxCosine = (maxCosine > cosine) ? maxCosine
										: cosine;
							}

							if (maxCosine < 0.3) {

								candidates.add(mp);
								
								
							}
						}
					}
				}

			}

		}

		 Imgproc.drawContours(localMat,candidates , -1, new
		 Scalar(0,255,0),2);
		
	
	return localMat;
	}
	
	private static double angle( Point pt1, Point pt2, Point pt0 )
	{
    double dx1 = pt1.x - pt0.x;
	    double dy1 = pt1.y - pt0.y;
    double dx2 = pt2.x - pt0.x;
    double dy2 = pt2.y - pt0.y;
	    return (dx1*dx2 + dy1*dy2)/Math.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
	}
	
	private static void saveMat(Mat inputMat,String name)
	{
		
      Bitmap picture = Bitmap.createBitmap((int)Math.ceil( inputMat.size().width), (int)Math.ceil( inputMat.size().height), Bitmap.Config.ARGB_8888);
       Utils.matToBitmap(inputMat, picture);
       
      try {
          FileOutputStream out = new FileOutputStream( getPah(name));
          picture.compress(Bitmap.CompressFormat.JPEG, 90, out);
          picture.recycle();
          
      } catch (Exception e) {
          e.printStackTrace();
      }
	}

	
	private static String getPah(String name)
	{
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-SSSS");
        String currentDateandTime = sdf.format(new Date());
        String fileName = Environment.getExternalStorageDirectory().getPath() +
                               "/Banzen/"+ name + currentDateandTime + ".jpg";
        
       
		return fileName;
	}
	
	private static void deskewMat(Mat inputMat, Mat outputMat,
			MatOfPoint2f points2f) {

		RotatedRect box = Imgproc.minAreaRect(points2f);
		Point[] pts = points2f.toArray();
		// box.points(pts);

		sortPoints(pts);

		MatOfPoint2f src_vertices = new MatOfPoint2f(pts[3], pts[2], pts[1]);
		MatOfPoint2f dst_vertices = null;
		if (pts[0].y <= box.center.y && pts[1].y >= box.center.y) // order
																	// 0,1,2,3
																	// starting
																	// from
																	// top-right
																	// vertex
		{

			dst_vertices = new MatOfPoint2f(new Point(0, 0), new Point(0, box
					.boundingRect().height - 1),
					new Point(box.boundingRect().width - 1,
							box.boundingRect().height - 1));
		} else {
			dst_vertices = new MatOfPoint2f(new Point(
					box.boundingRect().width - 1, 0), new Point(0, 0),
					new Point(0, box.boundingRect().height - 1));
		}

		List<MatOfPoint> srcs = new ArrayList<MatOfPoint>();
		MatOfPoint mp = new MatOfPoint();
		mp.fromArray(pts);
		srcs.add(mp);

//		Mat draw = inputMat.clone();
//		saveMat("dr", draw);

		Mat warpAffineMatrix = Imgproc.getAffineTransform(src_vertices,
				dst_vertices);
		Mat rotated = new Mat();

		Size sz = new Size(box.boundingRect().width, box.boundingRect().height);
		Imgproc.warpAffine(inputMat, rotated, warpAffineMatrix, sz,
				Imgproc.INTER_LINEAR);
		
		

		rotated.copyTo(outputMat);

	}
	
	private static void sortPoints(Point[] pts)
	{
		
		
		double meanx=0,meany=0;
		for (int i=0;i<pts.length;i++)
		{
			meanx+=pts[i].x;
			meany+=pts[i].y;
				
			
		}
		meanx/=pts.length;
		meany/=pts.length;
			
		
		java.util.Arrays.sort(pts, new PointComparator(meanx, meany));
		
	}
	

	private static List<ContourData>  extractCandidatesFromPlate(Mat deskewedLicensePlate)
	{
		List<ContourData> result= new ArrayList<ContourData>();
		Mat rotated=new Mat();
		Mat rotatedCopy = new Mat();
		
		deskewedLicensePlate.copyTo(rotated);
		
		
		Imgproc.GaussianBlur(rotated, rotated, new Size(5, 5), 0);
		Imgproc.threshold(rotated, rotatedCopy, 100, 255,Imgproc.THRESH_BINARY);
		Imgproc.threshold(rotated, rotated, 100, 255,Imgproc.THRESH_BINARY_INV);
		
		
		
		Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ERODE,
				new Size(4, 4), new Point(1, 1));
		Imgproc.erode(rotated, rotated, kernel);
	
		kernel = Imgproc.getStructuringElement(Imgproc.MORPH_DILATE, new Size(
				21, 3), new Point(-1, -1));
		Imgproc.dilate(rotated, rotated, kernel);
		Imgproc.threshold(rotated, rotated, 100, 255, Imgproc.THRESH_BINARY);
		
		
		
		List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
		Imgproc.findContours(rotated.clone(), contours, new Mat(),
				Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

		int i = 0;
		for (MatOfPoint cntr : contours) {

			MatOfPoint2f orgCurve = new MatOfPoint2f();
			MatOfPoint2f approxCurve = new MatOfPoint2f();
			cntr.convertTo(orgCurve, CvType.CV_32FC2);

			Imgproc.approxPolyDP(orgCurve, approxCurve, 0.02 * Imgproc
					.arcLength(orgCurve, true), true);

			Rect r = Imgproc.boundingRect(cntr);
//			List<MatOfPoint> pnts = new ArrayList<MatOfPoint>();
//			MatOfPoint curve = new MatOfPoint();
//			orgCurve.fromArray(approxCurve.toArray());
//			pnts.add(curve);

			
			
			if (r.area() > 500 ) {

				
				Mat skMat = deskewedLicensePlate.submat(r);		
				
				result.add(new ContourData(r,skMat));

			}
		}
		
		return result;
	}

	
	
	
	
}
